#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Purpose: Units management
# Created: 02/04/2009
# $Id $
# Copyright (c) 2008,2009 by University College London
# Authors:
# Didrik Pinte --<dpinte@dipole-consulting.com>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.

'''
This module define the units supported by HydroPlatform. The common units 
generally used for water management will be declared here.

We could think about using general libraries for unit conversion.

See Unum :http://home.scarlet.be/be052320/Unum.html, or
    Magnitude : http://www.juanreyero.com/magnitude/ or
    
TODO : unit management in the database should be done using unit id in place of
strings ...
'''

import datetime
from dateutil.relativedelta import relativedelta

from enthought.traits.api import HasStrictTraits, List, Str, ListStr
from enthought.traits.ui.api import View, Item, CheckListEditor

DEFAULT_UNITS = {
          u' m' :   (u'meter', u'distance'),
          u' kW' :  (u'kilowatt', u'power'),
          u' kWh' : (u'kilowatt-hour', u'energy'),
          u' MWh' : (u'Megawatt-hour', u'energy'),
          u' GWh' : (u'Gigawatt-hour', u'energy'),
          u' hm3' : (u'million cubic meters', u'volume'),
          u' ML' : (u'ML', u'volume'),
          u' TAF' : (u'TAF', u'volume'),
          u' AF' : (u'AF', u'volume'),
          u' Mg' : (u'Mg', u'volume'),
          u' km3' : (u'Cubic kilometers', u'volume'),
          u' hm3/month' : (u'billion cubic meter per month', u'flow'),
          u' cfs' : (u'cfs', u'flow'),
          u' Mgd' : (u'Mgd', u'flow'),
          u' ML/day' : (u'ML/day', u'flow'),
          u' m3/s' : (u'Cubic meter per second', u'flow'),
          u' m/day' : (u'Meter per day', u'flow'),
          u' mm/day' : (u'Millimiter per day', u'flow'),
          u' %' : (u'Percentage', u'other'),
          u' $' : (u'Dollar', u'other'),
          u' €' : (u'Euro', u'other'),
          u' £' : (u'Pound', u'other'),
          u"\u00A5": (u'Yen', u'other'),
          u" sqm": (u'Square meters', u'area'),
          u" ha": (u'Square meters', u'agricultural area'),
          u" acres" : (u"Acres" , u"area"),
          u" km2" : (u"Square kilometers" , u"area"),
          u" sqft" : (u"Square foot", u"area"),
          u" second" : (u"Seconds", u"time"),
          u" minute" : (u"Minutes", u"time"),
          u" hour" : (u"Hours", u"time"),
          u" day" : (u"Days", u"time"),
          u" week":(u"Weeks", u"time"),
          u" month" : (u"Months", u"time"),
          u" year" : (u"Years", u"time"),
}

class Unit(object):
    
    def __init__(self, name, description, category):
        self.name = name
        self.description = description
        self.category = category
        

class Units(HasStrictTraits):
    '''
    List of units available
    '''
    available_units = List(Str, ["none"])
    

class UnitCategory(HasStrictTraits):
    '''
    Defines a category of units.
    '''

    name = Str
    selected_units  = List(label="Units")
    available_units = List
   
    traits_view = View(Item('selected_units', 
                            editor=CheckListEditor(name="available_units"),
                            style="custom")
                       )

def get_unit_categories(dbsession, project = None):
    '''
    Return a list of UnitCategory based on defined units in database
    '''
    existing_units = dbsession.query(Unit).all()
    if project is not None:
        visible_units = project.units
    else: visible_units = []
    categories = dict()
    for unit in existing_units:
        if not categories.has_key(unit.category):
            categories[unit.category] = UnitCategory(name=unit.category)
        categories[unit.category].available_units.append(unit.name)
        if unit in visible_units:
            categories[unit.category].selected_units.append(unit.name)
    return categories.values()
    
loaded_units = Units()

def load_units(dbsession, project):
    '''
    Returns all the list of units defined in the dbsession
    '''    
    loaded_units.available_units = ["-"]
    for unit in project.units:
        loaded_units.available_units.append(unit.name)    
    loaded_units.available_units.sort()
    
def create_default_units(dbsession):
    '''
    Create the default units in sqlalchemy dbsession
    '''
    for key, (desc, category) in DEFAULT_UNITS.items():
        
        if dbsession is not None:
            # search if already existing
            eunit = dbsession.query(Unit).filter(Unit.name == key).first()
        if eunit is None:
            # create the unit
            eunit = Unit(key, desc, category)
            dbsession.add(eunit)
    dbsession.commit()


DAILY        = 1
WEEKLY       = 2
BIWEEKLY     = 3
MONTHLY      = 4
SEASONNALITY = 6
YEARLY       = 7
CUSTOM       = 8


class TimeStep(object):
    
    def __init__(self, tid, name, relativedelta, shortformat):
        self.tid = tid
        self.name = name
        self.relativedelta = relativedelta
        self.shortformat = shortformat
    
TIMESTEPS = {DAILY       : TimeStep(DAILY, u'daily', 
                                    {'days' : 1}, "%j"),
             WEEKLY      : TimeStep(WEEKLY, u'weekly', 
                                    {'weeks' : 1}, "%U"),
             BIWEEKLY    : TimeStep(BIWEEKLY, u'bi-weekly', 
                                    {'weeks' : 2}, "%U"),
             MONTHLY     : TimeStep(MONTHLY, u'monthly', 
                                    {'months' : 1}, "%b"),
             SEASONNALITY : TimeStep(SEASONNALITY, u'seasonally', 
                                     {'months' : 6}, "%b"),
             YEARLY      : TimeStep(YEARLY, u'yearly', 
                                    {'years' : 1}, "%Y"),
             CUSTOM      : TimeStep(CUSTOM, u'custom', None, None)}


def getDateSerie(fromdate, todate, timestep_int=0, step=None, amount=0):
    '''
    Returns a list of date object based on the starting fromdate to the ending
    todate with a given timestep. The timestep is taken in the TIMESTEPS dict
    and must be compliant with the dateutil.relativedelta syntax
    '''
    serie = [fromdate]
    cdate = fromdate    
    if timestep_int > 0:
        kwargs = TIMESTEPS[timestep_int]
        if kwargs is None:
            raise Exception("No timedelta set for %s" % timestep)
        params = kwargs.relativedelta
    else:
        params = {step: amount}
        
    cdate = cdate + relativedelta(**params)
    while cdate <= todate:
        serie.append(cdate)
        cdate = cdate + relativedelta(**params)
    return serie
